//对比any
var info0 : any;
info0 = 123;
info0 = "aaa";
info0 = true;
//unknown 未知类型
var info1 : unknown;
info1 = 123;
info1 = "ccc";
info1 = false;

//区别：
// var info2:number;
// info2 = info0;//any赋值给string
// info2 = info1;//类型安全限制，不能给其他类型赋值

//void类型：没有结果
//never永远没有类型，不会执行到函数的最后，通常当函数[没有正常返回结果]报错时使用
function method1():void{
    console.log('xxxx');
    //return 'aaa';// 不能有返回结果的
    //return null;//  不能有返回结果的
    //return undefined;//不报错
}
function method2():never{
    throw new Error("自定义错误！");
    //return undefined;//报错
}
//var result = method1();
//console.log(result);
//异常处理机制：try{}  catch(){} 
//尝试执行try，如果没有问题就直接继续执行异常处理机制后面代码，跳过catch
//尝试执行try，如果有问题，从出错行到try结束的}位置包含的所有代码全部跳过[不执行]，执行catch，再执行异常处理机制后面代码
try{
    //var result = method2();
    //console.log(result);
    console.log("正确代码");
}catch(err:any){
    console.log(1);
}finally{
    console.log("不管程序是否出错都一定会执行的代码");
}
console.log("异常处理机制代码之后");

// | & 运算符
var info2:string | number; //不能确定类型时，写多个。结合typeof
var info3:string & number;//语法正确的，只是找不到这样的数据而已

var info4:{name:string} & {age:number};//在对象类型中合并属性
//info4 = {name:'abc'};//报错
info4 = {name:'abc',age:18};//正确的


//面向对象
//原生js: 没有类的概念
//   基于object创建对象  直接赋值
//   基于字面量创建对象   直接赋值
//   使用构造函数创建对象   function GouZao(){}
//   使用原型创建对象
//       使用函数描述特征，属性存放数据，函数实现功能
//   使用Object.create根据原型创建对象

//ES6/TS：
//     创建class类
//     实例化对象：new 类名()

//创建类：
// class Student{
//     stuName="三藏";
//     age;
//     show(){
//     }
// }
//类名：推荐帕斯卡命名法
//实力化对象：
//var stu = new Student();

//创建类
class Pet{
    petName:string="狗狗";
    petAge:number=2;
    show(){
        console.log(this.petName,this.petAge);
    }
}
//实例化对象
var dog = new Pet();
dog.show();


//ts中面向对象的：
//   实物的特征描述到类中  会创建类
//   使用属性描述静态特征--> 数据
//   使用方法描述动态特征--> 业务逻辑功能
//   使用面向对象的步骤：
//   1、创建类
//      class 类名{}
//   2、编写属性
//      修饰符 属性名:类型;
//      修饰符：public公共的 / private私有的
//      属性名：驼峰命名法
//   3、编写方法
//      修饰符 方法名(参数列表){
//           逻辑代码
//      }
//      修饰符：public公共的 / private私有的
//      方法名：驼峰命名法
//      参数列表：必须放在()里，形参和实参的个数、类型、顺序必须一致
//   4、实例化对象
//      var 对象名 = new 类名();
//      对象名.属性名
//      对象名.方法名();

// 定义类
class Student { 
    //定义属性
    public stuNo:number;//公有属性
    private stuName:string;//私有属性

    //构造函数[特殊方法]：
    //   作用一：为实例化对象提供方式[只能显示的写一个]
    //   作用二：为属性赋初始值

    //有参构造函数
    constructor(stuNo:number,stuName:string){
        this.stuNo = stuNo;
        this.stuName = stuName;
        //不会写别的逻辑
    }
    // 无参构造函数
    // constructor(){
    // }

    //定义方法
    show(){
        console.log("student对象的show方法！ 学号：" + this.stuNo + "，姓名：" + this.stuName);
    }
    test(){
        this.show();
    }
    method1(str1:string){
        console.log("str1: " + str1);
    }
    method2(str2:string, num2:number){
        console.log("str2: " + str2 + "，num2：" + num2);
    }
    method3(str3:string):boolean{
        return str3.length>3? true:false;
    }
}

//实例化对象
var stu1 = new Student(1001,"zhangsan");
stu1.stuNo = 1003;
////stu1.stuName = 'zhangsan'; // 私有不能使用
stu1.show();
// stu1.method1('aaa');
// stu1.method2('aaa',10);
// var result = stu1.method3('aaa');

var stu2 = new Student(1002,'lisi');
// stu2.stuName = 'wangwu';
// console.log("stu2.stuNo: " + stu2.stuNo);
stu2.show();

//注意：
//   同一个类中，在方法中使用属性或其他方法时，通过this关键字调用
//   this表示当前对象，可以调用：this.属性、this.方法()
//   修饰符：作用是约束使用范围
//          public公共的，表示任意位置都可使用，不写的默认值
//          private私有的，表示只能在当前类中使用
//          protected在继承关系的类中使用 [父类中和子类中可以使用，即使是测试调用写在类之外也不能]
//   参数列表：
//         定义时 [形参]
//            参数名:类型,参数名:类型
//         调用时 [实参]
//            值,值
//         要求：形参和实参的个数、类型、顺序保持一致。
//   构造函数：为创建对象提供方式，给属性赋值
//       没有写构造函数时，系统会默认提供一个无参的构造函数。
//       写了，系统就不提供了。构造函数只能显示的写一个。
//       语法：constructor(参数列表){}
//       有参构造函数的逻辑代码通常是给属性赋值。
//       有且只有一个

// 面向对象三大特征：封装、继承、多态
// [一]封装：
//     将数据存储到属性中[属性用于存储数据的]
//     将业务逻辑存储到方法中[方法用于实现功能]
//     将描述的对象特征存储到类中[类用于描述特征]

// [二]继承：
//     即使子类中的特征没写，通过继承依然能够获取使用。
//     回顾js中的继承：
//        子类构造函数名.prototype = new 父类构造函数名();
//        实例对象.__proto__ = new 父类构造函数名
//        子类构造函数名.prototype = 父类构造函数名.prototype
//        遍历prototype特征赋值
//     TS中的继承：
//        子类 extends 父类
// 注意：
//     如果类中没有写构造函数，系统会默认提供一个无参构造函数；如果写了就不提供
//     使用继承是需要满足里氏替换原则(is a关系)：父类中特征，子类都能具有
//     并不是所有的父类特征都能被子类继承：
//          private修饰符修饰的特征不能被子类拿到
//          构造函数是每个类自己的特征，子类也拿不到
//     如果子类继承父类，那么子类一定会优先调用父类构造函数，再调用子类构造函数。
//         不管父类是有参还是无参，在子类构造函数的第一行写super调用
//     继承父类的特征后，子类也会有一套属于子类自己的特征
//     super作用：调用父类特征
//               1) super()     调用父类构造函数
//               2) super.特征  调用父类的属性和方法
//     对比this和super
//          this表示当前对象[看写在哪]，super表示父类对象
//     继承的特征：
//          单根性[只能有一个父类]    传递性[特征可以一直传递给子类的子类]

// [三]多态：
//         同一个功能在不同的情况下会产生不同的作用
//         只有方法有多态，属性是没有多态的
//      实现方式：
//          1) 方法的重载 [TS不支持]
//                 同一个类中，方法名相同，参数列表不同[个数不同、类型不同、顺序不同]的多个方法。
//                 与 修饰符无关、返回值类型无关、参数名无关。
//          后面两种要求基于继承。
//          2) 方法的重写
//                 父类中有的方法，在子类中重写一个。
//                 父子类中，拥有方法名相同、参数列表相同[个数相同、类型相同、顺序相同]的方法
//                 与 方法的返回值，参数名无关、与除private修饰符以外的修饰符无关
//                 子类重写父类之后，程序会优先调用子类的方法，而不是父类的。
//                 父类中的特征用于提供默认实现。
//          3) 子类替换父类
//                 编译时的类型和运行时的类型不一致就会产生多态。
//                 方式一：父类作为参数类型，被子类替换
//                 方式二：父类作为返回值类型，被子类替换

//父类
class Traffic{
    public mc:string;
    protected sizes:number;

    //有参构造函数
    constructor(mc:string, sizes:number){
        this.mc = mc;
        this.sizes = sizes;
        console.log("父类构造函数")
    }

    //参构造函数
    // constructor(){
    // }

    //方法
    public goHome(){
        console.log("走！回家~~")
    }
}

class Car{

}

//子类
class Bus extends Traffic{
    constructor(mc:string, sizes:number){
        super(mc,sizes);
        console.log("子类构造函数")   
    }

    public test1(){
        console.log("子类方法中调用父类方法")
        this.goHome();
        super.goHome();
        console.log(this.sizes);
    }
}

//父类可以派生多个子类
class DiTie extends Traffic{

}

//子类的子类
class No11Bus extends Bus{
    constructor(mc:string, sizes:number){
        super(mc,sizes);
    }
}

//实例化对象
var bus = new Bus("地铁",10000);
console.log(bus.mc);
//console.log(bus.sizes);
bus.goHome();

var n11 = new No11Bus("11路",50);
n11.mc="";
n11.goHome();

//-------------------------------

//TS不支持方法的重载
// class MyClass{

//     public show(){
//         console.log("第一个方法")
//     }

//     public show(str:string){
//         console.log("第二个方法")
//     }
// }
// var mc = new MyClass();
//mc.show("");
/*
class Father{

    public show(info:string, num:number):void{
        console.log("父类中的方法")
    }

    protected test(){
        console.log("父类中的方法")
    }
}

class Son extends Father{

    public show(str:string, count:number, ):string{
        console.log("子类中的方法")
        return "";
    }

    public test(){
        console.log("子类中的方法")
    }
}

var obj = new Son();
obj.show("",1);
obj.test();
*/

class YinLiao{

    public kouWei(){
        console.log("饮料是甜的！")
    }
}

class JiaDuoBao extends YinLiao{

    public kouWei(){
        console.log("加多宝是不怕上火！")
    }
}

class LeHu extends YinLiao{

    public kouWei(){
        console.log("乐虎是功能饮料！")
    }
}

class Person{

    public buy(yl:YinLiao){
        yl.kouWei();
    }
    // public buy2(yl:LeHu){

    // }
}

class ShouMaiJi{

    public sell(num:number):YinLiao{
        var fh;
        if(num == 1){
            fh = new JiaDuoBao();
        }else{
            fh = new LeHu();
        }
        return fh;
    }
    // public sell2():LeHu{
    //     return new LeHu();
    // }
}

var jdb = new JiaDuoBao();
var lh = new LeHu();

var per = new Person();
per.buy(jdb);
console.log("-----------------------")
var smj = new ShouMaiJi();
var result = smj.sell(2);
result.kouWei();

//-------------------------------------------------------
//家用电器信息包含：品牌、功率属性，功能有耗电。
//现要求使用面向对象描述你家里的所有电器信息，保存到数组中。
//[空调[有外机]、彩电[颜色]、冰箱[几个门]]

/**
 * 家用电器父类
 */
class JYDQ{
    public pinpai:string;
    public gongLv:string;

    constructor(pinpai:string, gongLv:string){
        this.pinpai = pinpai;
        this.gongLv = gongLv;
    }

    public hd(){
        console.log("家用电器正在耗电。。。")
    }
}

/**
 * 空调子类
 */
class KongTiao extends JYDQ{

    //是否有外机
    public wj:boolean;

    constructor(pinpai:string, gongLv:string, wj:boolean){
        super(pinpai, gongLv)
        this.wj = wj;
    }

    public hd(){
        console.log("是否有外机？"+this.wj+" 的空调正在耗电。。。房间正在边凉！")
    }
}

/**
 * 彩电子类
 */
 class CaiDian extends JYDQ{

    //黑白还是彩色
    public yanse:string;

    constructor(pinpai:string, gongLv:string, yanse:string){
        super(pinpai, gongLv)
        this.yanse = yanse;
    }

    public hd(){
        console.log("有"+this.yanse+"的彩电正在耗电。。。节目还行！")
    }
}

/**
 * 冰箱子类
 */
 class BingXiang extends JYDQ{

    //几门
    public jimen:number;

    constructor(pinpai:string, gongLv:string, jimen:number){
        super(pinpai, gongLv)
        this.jimen = jimen;
    }

    public hd(){
        console.log("有"+this.jimen+"门的冰箱正在耗电。。。水果很新鲜！")
    }
}

var dqArr:JYDQ[];
var kt = new KongTiao("格力","2000W",true);
var cd = new CaiDian("长虹","1000W","五彩缤纷");
var bx = new BingXiang("海尔","800W",2);
dqArr = [kt,cd,bx];

for(var i=0; i<dqArr.length; i++){
    dqArr[i].hd();
}